﻿using System;
using System.Collections.Generic;
using System.Linq;
using CardPlay.Model;

namespace CardPlay.DemoPack.Games.PyramidVariants
{
    public class TriangularArray
    {
        public TriangularArray(int height)
        {
            Guard.Against<ArgumentOutOfRangeException>(height <= 0, "depth");
            Height = height;
        }

        public int Height { get; private set; }

        public int Count { get { return IndexFromRow(Height); } }

        public int RowFromIndex(int index)
        {
            return (index >= 0)
                ? Enumerable.Range(0, Height).Select(row => Height - row).FirstOrDefault(row => index >= IndexFromRow(row))
                : -1;
        }

        public IEnumerable<int> ParentIndicesOf(int index)
        {
            if (index == 0) yield break;

            var rowOfIndex = RowFromIndex(index);
            var startOfRow = IndexFromRow(rowOfIndex);

            if (index > startOfRow)
                yield return index - rowOfIndex - 1;    // Left parent

            if (index < startOfRow + rowOfIndex)
                yield return index - rowOfIndex;        // Right parent
        }

        public IEnumerable<int> ChildIndicesOf(int index)
        {
            var rowOfIndex = RowFromIndex(index);
            if (rowOfIndex + 1 == Height) return new int[] { };

            var childIndex = IndexFromRow(rowOfIndex + 1) + (index - IndexFromRow(rowOfIndex));
            return new[] { childIndex, childIndex + 1 };
        }

        public static int IndexFromRow(int row)
        {
            return row * (row + 1) / 2;
        }

        public static int IndexFromRowColumn(int row, int column)
        {
            return IndexFromRow(row) + column;
        }
    }
    
    public class TriangularArray<T> : TriangularArray, IEnumerable<T>
    {
        private readonly T[] _elements;

        public TriangularArray(int height) : base(height)
        {
            _elements = new T[Count];
        }

        public int IndexWhere(Predicate<T> condition)
        {
            for (var i = 0; i < _elements.Length; ++i)
                if (condition(_elements[i])) 
                    return i;

            return -1;
        }

        public T this[int index]
        {
            get { return _elements[index]; }
            set { _elements[index] = value; }
        }

        public T this[int row, int column]
        {
            get { return _elements[IndexFromRowColumn(row, column)]; }
            set { _elements[IndexFromRowColumn(row, column)] = value; }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return _elements.Cast<T>().GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _elements.GetEnumerator();
        }
    }
}